package my.stone.asetdemo.common

import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import java.lang.IllegalArgumentException
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

fun <V: View> View.bindView(id: Int)
    : ReadOnlyProperty<View, V> = required(id, viewFinder)


private val View.viewFinder: View.(Int) -> View?
    get() = { findViewById(it) }


private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing =
    throw IllegalArgumentException("没有找到View id: $id, for ${desc.name}")

@Suppress("UNCHECKED_CAST")
private fun <T, V: View> required(
    id: Int,
    finder: T.(Int) -> View?,
    onInitializedListener: ((V) -> Unit)? = null
) = Lazy { t: T, desc ->
    t.finder(id) as V? ?: viewNotFound(id, desc)
}.apply {
    this.onInitializedListener = onInitializedListener
}

@Suppress("UNCHECKED_CAST")
private class Lazy<T, V>(
    private val initializer: (T, KProperty<*>) -> V
) : ReadOnlyProperty<T, V>, LifecycleObserver {

    var onInitializedListener: ((V) -> Unit)? = null

    private object EMPTY

    private var value: Any? = EMPTY
    private var attachedToLifecycleOwner = false

    override fun getValue(thisRef: T, property: KProperty<*>): V {
        checkAddToLifecycleOwner(thisRef)
        if (value == EMPTY) {
            value = initializer(thisRef, property)
        }

        val v = value as V
        onInitializedListener?.invoke(v)
        return v
    }

    private fun checkAddToLifecycleOwner(thisRef: T) {
        if (!attachedToLifecycleOwner && (thisRef is LifecycleOwner)) {
            thisRef.lifecycle.addObserver(this)
            attachedToLifecycleOwner = true
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() {
        value = EMPTY
    }
}